#include <sys/ioctl.h>
#include <sys/uio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <hi_type.h>
#include "utils_service.h"
#include "hw_app_config.h"
#include "hw_app_log.h"

// static std::mutex m_mutex;

UtilsService::UtilsService() {
 
}

UtilsService::~UtilsService()
{
    close(m_fd_utils);
    m_fd_utils = -1;
    LOG_DEBUG("UtilsService destory\n");
}

int UtilsService::GetFd()
{
    std::lock_guard<std::mutex> lockFd(m_mutex); //该句柄fd非线程安全，每次使用时，需要做线程安全保护
    if (m_fd_utils <= 0) {
       m_fd_utils = ServiceRegister();
    }

    return m_fd_utils;
}

int UtilsService::ServiceRegister()
{
    m_fd_utils = open(UTILS_SERVICE_NAME, O_RDWR);
    if (m_fd_utils <= 0) {
        LOG_ERROR("open %s failed, ret is: %i\n", UTILS_SERVICE_NAME, m_fd_utils);
        return -1;
    }
    return m_fd_utils;
}

int UtilsService::SDCGetHardWareId(sdc_hardware_id_s *pstHardWareParas)
{
    int fd = GetFd();	
    if (fd <= 0) {
        LOG_ERROR("open %s failed, ret is: %i\n", UTILS_SERVICE_NAME, fd);
        return -1;
    }   
	sdc_common_head_s head;	
	struct iovec iov[2] = 
    {
		[0] = {.iov_base = &head, .iov_len = sizeof(head) },
		[1] = {.iov_base = pstHardWareParas->id, .iov_len = sizeof(sdc_hardware_id_s) },
	};
	ssize_t retn;

	memset(&head,0,sizeof(head));
	head.version = SDC_VERSION;
	head.url = SDC_URL_HARDWARE_ID;
	head.method = SDC_METHOD_GET;
	head.trans_id = ++m_transId;
	head.head_length = sizeof(head);

	retn = write(fd, &head, sizeof(head));
	if(retn != sizeof(head)) {
		LOG_ERROR("write to config.iaas.sdc fail: %m\n");
		return ERR;
	}

	retn = readv(fd, iov, 2);
	if(retn == ERR) {
		LOG_ERROR("read from config.iaas.sdc fail!\n");
		return ERR;
	}

	if(head.code != SDC_CODE_200) {
		LOG_ERROR("read from config.iaas.sdc fail: code = %u\n",head.code);
		return ERR;
	}

	if(head.trans_id != m_transId) {
		LOG_ERROR("read from config.iaas.sdc fail: wrong trans-id= %u, expected = %u\n",head.trans_id, m_transId);
	}
    return HW_OK;
}

void UtilsService::SDCMemFree(sdc_mmz_alloc_s* pstMemParas)
{
    int fd = GetFd();
    if (fd <= 0) {
        LOG_ERROR("open %s failed, ret is: %i\n", UTILS_SERVICE_NAME, m_fd_utils);
        return;
    }
	sdc_common_head_s head; 
    head.version = SDC_VERSION;
    head.url = SDC_URL_MMZ;
    head.method = SDC_METHOD_DELETE;
    head.head_length = sizeof(head);
    head.content_length = sizeof(*pstMemParas);

	struct iovec iov[] = {
		{ (void*)&head, sizeof(head) },
		{ pstMemParas, sizeof(*pstMemParas) }
	};

	(void)writev(fd, iov, sizeof(iov)/sizeof(iov[0]));
    return ;
}

int UtilsService::SDCMemAlloc(unsigned int size, sdc_mmz_alloc_s* pstMemParas)
{
    int fd = GetFd();
    if (fd <= 0) {
        LOG_ERROR("open %s failed, ret is: %i\n", UTILS_SERVICE_NAME, fd);
        return -1;
    }
    sdc_common_head_s head;
    head.version = SDC_VERSION;
    head.url = SDC_URL_MMZ;
    head.method = SDC_METHOD_CREATE;
    head.head_length = sizeof(head);
    head.content_length = sizeof(size);

    struct iovec iov[] = {
        { (void*)&head, sizeof(head) },
        { &size, sizeof(size) }
    };

    int nret = writev(fd, iov, 2);
    if(nret < 0) return errno;

    iov[1].iov_base = pstMemParas;
    iov[1].iov_len = sizeof(*pstMemParas);

    nret = readv(fd, iov,2);
    if(nret < 0) 
    {
        fprintf(stdout,"mmz_alloc_cached:1\n");
        return errno;
    }

    if(head.code != SDC_CODE_200 || head.head_length != sizeof(head) || head.content_length != sizeof(*pstMemParas)) 
    {
        fprintf(stdout,"mmz_alloc_cached:2,size:%d\n", pstMemParas->size);
        return EIO;       
    }
    return size;
}

int UtilsService::SDCFlushCache(int64_t u64PhyAddr, void *pvVirAddr, uint32_t u32Size)
{    
    int fd = GetFd();	
    if (fd <= 0) {
        LOG_ERROR("open %s failed, ret is: %i\n", UTILS_SERVICE_NAME, m_fd_utils);
        return -1;
    }    
    int32_t s32Ret = HI_SUCCESS;
    sdc_mem_s sdc_mem_addr;
    sdc_mem_addr.addr_phy = (void *)u64PhyAddr;
    sdc_mem_addr.addr_virt = pvVirAddr;
    sdc_mem_addr.size = u32Size;

    s32Ret = ioctl(fd, SRVFS_PHYMEM_CACHEFLUSH, &sdc_mem_addr);
	return s32Ret;
}